package com.oracle.outsidein.pool;

import com.oracle.outsidein.OutsideinManagerConfig;
import com.oracle.outsidein.OutsideinUtils;
import com.oracle.outsidein.process.LinesPumpStreamHandler;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Callable;

@Slf4j
@Setter
public abstract class AbstractConvertTask implements ConvertTask, Callable<ConvertResult> {
    protected OutsideinManagerConfig config;
    protected ConvertTaskInfo info;
    protected File src;
    protected File dst;

    public AbstractConvertTask(ConvertTaskInfo info) {
        this.info = info;
    }

    @Override
    public ConvertResult call() throws Exception {
        long start = System.currentTimeMillis();

        src = new File(info.getSrcFile());
        if (!src.isFile()) {
            throw new RuntimeException("src not a file, " + info.getSrcFile());
        }
        dst = new File(info.getDstFile());

        convertBefore();

        log.info("oitType={} srcFile={} dstFile={}", info.getOitType(), info.getSrcFile(), info.getDstFile());
        log.info("src={} dst={}", src.getAbsolutePath(), dst.getAbsolutePath());

        ConvertResult convertResult = convert();

        if (convertResult.getCode() == 0) {
            convertAfter();
        }

        convertResult.setCost(System.currentTimeMillis() - start);

        return convertResult;
    }

    public abstract void convertBefore();

    @Override
    public ConvertResult convert() {
        final List<String> command = new ArrayList<>();
        final File executable = OutsideinUtils.getOfficeExecutable(config.getOutsideinHome(), config.toolDir(info.getOitType()));
        if (!config.getRunAsArgs().isEmpty()) {
            command.addAll(config.getRunAsArgs());
        }
        final String execPath = executable.getAbsolutePath();
        log.info("execPath={}", execPath);

        command.add(execPath);
        command.add(src.getAbsolutePath());
        command.add(dst.getAbsolutePath());
        command.add(config.getCfg(info.getOitType()));
        log.info("command={}", String.join(" ", command));
        int code = 0;
        final ProcessBuilder processBuilder = new ProcessBuilder(command);
        Process process;
        LinesPumpStreamHandler handler = null;
        ConvertResult convertResult = new ConvertResult();
        try {
            process = processBuilder.start();
            handler = new LinesPumpStreamHandler(process.getInputStream(), process.getErrorStream());
            handler.start();
            try {
                code = process.waitFor();
                log.debug("process exit, {}", code);
            } catch (InterruptedException e) {
                log.warn("process InterruptedException {}", e.getMessage());
                Thread.currentThread().interrupt();
            }
            handler.stop();
            process.destroyForcibly();
            convertResult.setCode(code);
            convertResult.setResult(code == 0 ? "success" : "failed");
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            convertResult.setCode(-1);
            convertResult.setResult(e.getMessage());
        }
        if (handler != null) {
            convertResult.setInfo(handler.getOutputPumper().getLines());
            convertResult.setError(handler.getErrorPumper().getLines());
        }
        return convertResult;
    }

    public abstract void convertAfter();

    public static void deleteDir(File file) {
        if (file.isDirectory()) {
            for (File f : Objects.requireNonNull(file.listFiles())) {
                deleteDir(f);
            }
            log.debug("del dir={}", file.getPath());
        } else {
            log.debug("del file={}", file.getPath());
        }
        file.delete();
    }
}
